package com.ahncnk.powermonitor.commons.exception;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import org.springframework.web.util.WebUtils;

import javax.security.sasl.AuthenticationException;
import java.lang.reflect.InvocationTargetException;
import java.util.regex.Pattern;

@ControllerAdvice
public class ResponsePlainExceptionHandler extends ResponseEntityExceptionHandler {
    private static final String DEFAULT_ERROR_MESSAGE = "系统繁忙，请稍后再试...";
    private static final MediaType PLAIN = MediaType.parseMediaType("text/plain;charset=utf-8");

    private final Logger logger = LoggerFactory.getLogger(getClass());


    @Override
    protected ResponseEntity<Object> handleExceptionInternal(Exception ex, @Nullable Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
        if (HttpStatus.INTERNAL_SERVER_ERROR.equals(status)) {
            request.setAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE, ex, WebRequest.SCOPE_REQUEST);
        }

        if (body == null) {
            body = ex.getMessage();
        }
        return new ResponseEntity<>(body, headers, status);
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity handle(Exception ex) {
        ResponseStatus annotation = AnnotationUtils.findAnnotation(ex.getClass(), ResponseStatus.class);
        HttpStatus status = null;
        String reason = null;
        if (annotation != null) {
            status = annotation.code();
            reason = annotation.reason();
        }
        if (StringUtils.isEmpty(reason)) {
            reason = reflectReasonFromException(ex);
        }
        if (status == null) {
            status = HttpStatus.INTERNAL_SERVER_ERROR;
        }
        logger.error("检测到异常", ex);
        return ResponseEntity.status(status)
                .contentType(PLAIN)
                .body(reason);
    }

    @ExceptionHandler(AuthenticationException.class)
    public Object handle(AuthenticationException ex) {
        String reason = ex.getMessage();
        reason = StringUtils.isEmpty(reason) ? "会话已过期，请重新登录……" : reason;

        logger.debug("登录失败：{}", reason);
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
                .contentType(PLAIN)
                .body(reason);
    }

    @ExceptionHandler(IllegalArgumentException.class)
    public Object handle(IllegalArgumentException ex) {
        logger.debug("参数错误：" + ex.getMessage(), ex);
        return ResponseEntity.badRequest()
                .contentType(PLAIN)
                .body(ex.getMessage());
    }

    @ExceptionHandler(IllegalStateException.class)
    public Object handle(IllegalStateException ex) {
        logger.error("系统状态异常", ex);
        String reason = cutFromExceptionMessage(ex);
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .contentType(PLAIN)
                .body(reason);
    }

    @ExceptionHandler(HttpClientErrorException.class)
    public Object handle(HttpClientErrorException ex) {
        String reason = cutFromExceptionMessage(ex);
        logger.debug("网关协议出错：{}", reason);
        return ResponseEntity.status(ex.getStatusCode())
                .contentType(PLAIN)
                .body(reason);
    }

    @ExceptionHandler(HttpServerErrorException.class)
    public Object handle(HttpServerErrorException ex) {
        logger.error("网关内部错误", ex);
        return ResponseEntity.status(HttpStatus.BAD_GATEWAY)
                .contentType(PLAIN)
                .body(DEFAULT_ERROR_MESSAGE);
    }

    @ExceptionHandler(ApplicationRuntimeException.class)
    public Object handle(ApplicationRuntimeException ex) {
        return handleApplicationException(ex.getReason(), ex.getStatus(), ex.getMessage(), ex.getCause());
    }

    @ExceptionHandler(AbstractApplicationException.class)
    public Object handle(AbstractApplicationException ex) {
        return handleApplicationException(ex.getReason(), ex.getStatus(), ex.getMessage(), ex.getCause());
    }

    private Object handleApplicationException(String reason, HttpStatus status, String message, Throwable cause) {
        if (cause == null) {
            logger.debug("业务异常[{} {}]：{}", status.value(), reason, message);
        } else {
            logger.info(String.format("业务异常[%s %s]: %s", status.value(), reason, message), cause);
        }
        return ResponseEntity.status(status)
                .contentType(PLAIN)
                .body(reason);
    }

    private String reflectReasonFromException(Exception ex) {
        try {
            return String.valueOf(ex.getClass()
                    .getMethod("getReason")
                    .invoke(ex));

        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            return DEFAULT_ERROR_MESSAGE;
        }
    }

    private static final Pattern CODE_BEGIN_REGEX = Pattern.compile("^\\d{3} .*");

    private static String cutFromExceptionMessage(Exception ex) {
        String message = ex.getMessage();
        if (message != null && CODE_BEGIN_REGEX.matcher(message).matches()) {
            message = message.substring(4);
        }
        if (message == null) {
            message = DEFAULT_ERROR_MESSAGE;
        }
        return message;
    }
}
